package com.yusys.pattern.mode.decorator.mybatis;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 阻塞式获取缓存，获取缓存时先获取锁，获取完释放锁，
 * 设置缓存不需要获取锁，锁如果是当前线程持有，那么就释放锁
 */
public class BlockingCache implements Cache {

    private final Cache delegate;
    private final ConcurrentHashMap<String, ReentrantLock> locks;

    public BlockingCache(Cache cache){
        this.delegate = cache;
        this.locks = new ConcurrentHashMap<>();
    }

    @Override
    public Object getCache(String key) {
        //获取锁
        acquireLock(key);
        Object cache = delegate.getCache(key);
        //获取数据成功释放锁
        if(cache != null){
            System.out.println("-------BlockingCache got cached key "+key);
            releaseLock(key);
        }
        return cache;
    }

    @Override
    public void putObject(String key, Object value) {
        try{
            delegate.putObject(key,value);
            System.out.println("-------BlockingCache puted cached key "+key);
        }finally {
            releaseLock(key);
        }

    }

    private void acquireLock(String key) {
        ReentrantLock lock = getLockForKey(key);
        //可以考虑 超时的加锁
       // lock.lock();
        lock.lock();


    }

    /**
     * 可重入的锁处理
     * @param key
     * @return
     */
    private ReentrantLock getLockForKey(String key) {
        //新建一把锁
        ReentrantLock lock = new ReentrantLock();
        //如果concurrentHashMap 当前不存在那么key，即当前key之前没有持有锁，那么把新lock设置给key
        //否则 返回之前key对应的lock
        ReentrantLock previous = locks.putIfAbsent(key, lock);
        return previous == null ? lock : previous;

    }

    /**
     * 释放锁，只有持有锁的线程才能释放锁
     * @param key
     */
    private void releaseLock(String key) {
        ReentrantLock reentrantLock = locks.get(key);
        if(reentrantLock.isHeldByCurrentThread()){
            reentrantLock.unlock();
        }
    }
}
